package com.anjiplus.template.gaea.business.modules.inf.inf.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.anji.plus.gaea.inf.constant.InfConstant;
import com.anji.plus.gaea.inf.exception.InfException;
import com.anji.plus.gaea.inf.extension.GaeaInfHepler;
import com.anji.plus.gaea.inf.module.dynamic.controller.dto.SqlModel;
import com.anji.plus.gaea.inf.module.dynamic.entity.CheckInfDTO;
import com.anji.plus.gaea.inf.util.InfAssert;
import com.anjiplus.template.gaea.business.modules.inf.app.controller.dto.InfAppDTO;
import com.anjiplus.template.gaea.business.modules.inf.app.dao.entity.GaeaInfAppRelation;
import com.anjiplus.template.gaea.business.modules.inf.app.service.GaeaInfAppRelationService;
import com.anjiplus.template.gaea.business.modules.inf.app.service.InfAppService;
import com.anjiplus.template.gaea.business.modules.inf.constant.InfCacheKey;
import com.anjiplus.template.gaea.business.modules.inf.constant.InfResCode;
import com.anjiplus.template.gaea.business.modules.inf.constant.InfStatusEnum;
import com.anjiplus.template.gaea.business.modules.inf.inf.dao.InfMapper;
import com.anjiplus.template.gaea.business.modules.inf.inf.dao.entity.InfEntity;
import com.anjiplus.template.gaea.business.modules.inf.inf.service.InfService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * @author ultrajiaming
 * @since 2021/3/29 10:12
 */
@Component
public class GaeaInfHelperImpl implements GaeaInfHepler {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private static final int INF_TEST = 0;
    private static final int INF_EXECUTE = 1;

    @Autowired
    private InfAppService infAppService;

    @Autowired
    private InfService infService;

    @Autowired
    private InfMapper infMapper;

    @Autowired
    private GaeaInfAppRelationService infAppRelationService;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public String getAppSecret(String appId) {
        InfAppDTO infAppDTO = infAppService.selectByAppId(appId);
        return null == infAppDTO ? null : infAppDTO.getAppSecret();
    }

    @Override
    public CheckInfDTO checkInfExecute(CheckInfDTO checkInf) {
        return this.checkInf(checkInf, INF_EXECUTE);
    }

    @Override
    public CheckInfDTO checkInfTest(CheckInfDTO checkInf) {
        return this.checkInf(checkInf, INF_TEST);
    }

    private CheckInfDTO checkInf(CheckInfDTO checkInf, int testFlag) {
        String infName = checkInf.getInfName();
        InfAssert.notEmpty(infName, InfResCode.INF_NAME_NOT_NULL);
        InfEntity infEntity = infService.getByName(infName);
        InfAssert.notNull(infEntity, InfResCode.INF_NOT_EXIST);
        // 校验状态
        if (INF_TEST == testFlag) {
            InfAssert.isTrue(infEntity.getStatus() == InfStatusEnum.PUBLISHED.getValue(), InfResCode.PUBLISHED_CANNOT_TEST);
        } else {
            InfAssert.isTrue(infEntity.getStatus() != InfStatusEnum.PUBLISHED.getValue(), InfResCode.ONLY_PUBLISHED_CAN_EXECUTE);
        }

        // 校验是否授权
        String appId = checkInf.getAppId();
        if (StringUtils.isNotBlank(appId)) {
            String redisKey = InfCacheKey.AUTHORIZED_APP + infName;
            Set<String> members = stringRedisTemplate.opsForSet().members(redisKey);
            if (!CollectionUtils.isEmpty(members) && !members.contains(appId)) {
                throw InfException.code(InfResCode.INTERFACE_NOT_AUTHENTICATED);
            }
        }

        // 校验调用次数
        if (INF_EXECUTE == testFlag) {
            LambdaQueryWrapper<GaeaInfAppRelation> wrapper = Wrappers.<GaeaInfAppRelation>lambdaQuery()
                    .eq(GaeaInfAppRelation::getInfName, infName)
                    .eq(GaeaInfAppRelation::getAppId, appId);
            List<GaeaInfAppRelation> list = infAppRelationService.list(wrapper);
            if (!CollectionUtils.isEmpty(list)) {
                GaeaInfAppRelation gaeaInfAppRelation = list.get(0);
                Integer times = gaeaInfAppRelation.getTimes();
                if (times == null || --times < 0) {
                    throw InfException.code(InfResCode.CALL_NUMBER_IS_OVER);
                }
            }
        }

        String responseParams = infEntity.getResponseParams();
        List<JSONObject> jsonArray = JSON.parseArray(responseParams, JSONObject.class);
        String sqlResults = null;
        if (!CollectionUtils.isEmpty(jsonArray)) {
            sqlResults = jsonArray.stream().filter(js -> js.get(InfConstant.RESPONSE_PARAM_KEY_NAME) != null).map(js -> {
                String name = js.get(InfConstant.RESPONSE_PARAM_KEY_NAME).toString();
                Object mapObj = js.get(InfConstant.PARAM_KEY_MAP);
                String mapping;
                StringBuilder sb = new StringBuilder(" ");
                sb.append(name).append(" as ");
                if (mapObj == null) {
                    mapping = com.baomidou.mybatisplus.core.toolkit.StringUtils.underlineToCamel(name);
                } else {
                    mapping = String.valueOf(mapObj);
                }
                sb.append(mapping);
                return sb.toString();
            }).collect(Collectors.joining(","));
        }
        checkInf.setInfDescription(infEntity.getInfDescription());
        checkInf.setSqlModel(new SqlModel(infEntity.getSqlSentence(), infEntity.getDynamicCondition(), sqlResults));
        return checkInf;
    }


    @Override
    public void postProcessRunTest(String infName, Supplier<String> supplier) {
        // 修改接口状态为测试通过
        if (StringUtils.isBlank(infName)) {
            logger.info("入参接口名称为空");
            return;
        }
        InfEntity infEntity = infService.getByName(infName);
        if (infEntity == null) {
            logger.info("接口不存在，infName={}", infName);
            return;
        }
        infEntity.setStatus(InfStatusEnum.TEST_PASS.getValue());
        String responseDemo = supplier.get();
        if (StringUtils.isNotBlank(responseDemo)) {
            infEntity.setResponseDemo(responseDemo);
        }
        infMapper.updateById(infEntity);
    }

    @Override
    public boolean checkAppIdSecret(String appId, String appSecret) {
        String redisKey = InfCacheKey.APP_SECRET + appId;
        ValueOperations<String, String> valueOps = stringRedisTemplate.opsForValue();
        String redisAppSecret = valueOps.get(redisKey);
        if (com.baomidou.mybatisplus.core.toolkit.StringUtils.isBlank(redisAppSecret)) {
            String realAppSecret = this.getAppSecret(appId);
            if (com.baomidou.mybatisplus.core.toolkit.StringUtils.isBlank(realAppSecret)) {
                valueOps.set(redisKey, "-1", new Random().nextInt(30), TimeUnit.MINUTES);
            } else if (appSecret.equals(realAppSecret)) {
                valueOps.set(redisKey, realAppSecret);
                return true;
            }
        } else if (redisAppSecret.equals(appSecret)) {
            return true;
        }
        return false;
    }
}
